/*
将字符串转为一个个token(即最小的不可再分的单元)
如: <div 视为一个token, 不可将其分成 <, div
class='aa' 有三个token, 分别是 class, =, 'a'
@click="add" 有三个token, 分别是 @click, =, 'add'
*/
export const ScannerType = {
    // <div
    OPENTAG: "OPENTAG",
    // </div
    OPENSLASHTAG: "OPENSLASHTAG",
    // 空白
    WHITESPACE: "WHITESPACE",
    // >
    CLOSETAG: "CLOSETAG",
    // />
    CLOSESLASHTAG: "CLOSESLASHTAG",
    // /
    SLASH: "SLASH",
    // =
    EQUAL: "EQUAL",
    // 字符串
    STRING: "STRING",
    // .
    DOT: "DOT",
    // @click v-bind:class :class class
    ATTRKEY: "ATTRKEY",
    TEXT: "TEXT",
    // {{ count }}
    TEMPLATE: "TEMPLATE",
    // 结束标记
    END: "END"
};

class Token{
    constructor(type, value, start, end) {
        this.type = type;
        this.value = value;
        this.raw = value;
        this.start = start;
        this.end = end;
        this.transForm();
    }
    transForm(){
        switch(this.type){
            // <div this.value ===> div
            case ScannerType.OPENTAG:
                this.value = this.value.slice(1);
                break;
            // </div this.value ===> div
            case ScannerType.OPENSLASHTAG:
                this.value = this.value.slice(2);
                break;
            // "'aaa'"  this.value ===>  'aaa'
            case ScannerType.STRING:
                this.value = this.value.slice(1, -1);
                break;
            case ScannerType.TEMPLATE:
                this.value = this.value.slice(2, -2).trim();
                break;

        }
        
    }
}
export class Scanner{
    constructor(template){
        this.template = template.replace(/&amp;/g, '&');
        this.position = 0;
        // 两个标志位,可以知道判断是属性节点与文本节点
        this.openTag = false;
        this.closeTag = false;
        this.tokens = [];
    }
    get currentChar(){
        return this.peek(0);
    }
    peek(offset){
        // 扫完了，返回给 \0 特殊字符告诉完毕了
        if (this.position+offset >= this.template.length) return '\0';
        return this.template[this.position+offset];
    }
    advance(offset=1){
        this.position+=offset;
    }
    slice(start, end){
        return this.template.slice(start, end);
    }
    get end(){
        if (this.currentChar === '\0') return true;
        return false;
    }
    scan(){
        while(!this.end){
            const start = this.position;
            const tokenType = this._scan();
            const end = this.position;
            const value = this.slice(start, end);
            if (tokenType && tokenType !== ScannerType.WHITESPACE){
                this.tokens.push(new Token(tokenType, value, start, end))
            }
        }
        this.tokens.push(new Token(ScannerType.END, '\0', 0, 0))
        return this.tokens;
    }
    _scan(){
        switch(this.currentChar){
            case '<':
                {
                    this.openTag = true;
                    this.closeTag = false;
                    // <div> </div>
                    this.advance()
                    let isOpenSlash = false;
                    if (this.currentChar==='/'){
                        isOpenSlash = true;
                        this.advance();
                    }
                    // 现在扫描的是 div
                    while(/\w|-/.test(this.currentChar)){
                        this.advance();
                    }
                    if (isOpenSlash){
                        return ScannerType.OPENSLASHTAG;
                    }
                    return ScannerType.OPENTAG;
                }
            case '>':
                this.openTag = false;
                this.closeTag = true;
                this.advance();
                return ScannerType.CLOSETAG;
            case '/':
                {
                    this.advance();
                    if (this.currentChar === '>'){
                        this.openTag = false;
                        this.closeTag = true;
                        this.advance();
                        return ScannerType.CLOSESLASHTAG;
                    }
                    return ScannerType.SLASH;
                }
            case '':
            case ' ':
            case '\r':
            case '\n':
            case '\t':
                this.advance();
                while(/\s/.test(this.currentChar)){
                    this.advance();
                }
                return ScannerType.WHITESPACE;
            case '=':
                this.advance();
                return ScannerType.EQUAL;
            case "'":
            case '"':
                {
                    const pair = this.currentChar;
                    this.advance();
                    while(this.currentChar !== pair && !this.end){
                        this.advance();
                    }
                    this.advance();
                    return ScannerType.STRING;
                }
            case '.':
                this.advance();
                return ScannerType.DOT;
            default:
                {
                    // 属性节点
                    if (this.openTag && !this.closeTag){
                        while(/\w|-|:|@/.test(this.currentChar)){
                            this.advance();
                        }
                        return ScannerType.ATTRKEY;
                    }
                    let isTemplate = false;
                    while(!this.end){
                        const start = this.position;
                        if ((this.currentChar === '{' &&
                            this.peek(1) === '{'
                        )){
                            this.scanTemplate();
                            // if (this.currentChar === '}' && this.peek(1)==='}'){
                            //     this.advance(2);
                            //     break;
                            // }
                            // this.advance();
                        }else{
                            this.scanText();
                        }
                        if ((this.currentChar === '<' && /[a-zA-Z|/]/.test(this.peek(1)))
                        ){
                            break;
                        }
                    } 
                }
        }
    }
    scanText(){
        const start = this.position;

        while(!this.end){
            if (this.currentChar==='{'&&this.peek(1)==='{')break;
            if ((this.currentChar === '<' && /[a-zA-Z|/]/.test(this.peek(1)))
                ){
                break;
            }
            this.advance();
        }

        const end = this.position;
        const content = this.slice(start, end);
        this.tokens.push(new Token(ScannerType.TEXT, content, start, end));
    }
    scanTemplate(){
        let templateClosed = false;
        const start = this.position;
        while(!this.end){
            if (this.currentChar === '}' && this.peek(1)==='}'){
                templateClosed = true;
                this.advance(2);
                break;
            }
            this.advance();
        }
        const end = this.position;
        const content = this.slice(start, end);
        if (templateClosed){
            this.tokens.push(new Token(ScannerType.TEMPLATE, content, start, end));
        }else{
            this.tokens.push(new Token(ScannerType.TEXT, content, start, end));
        }
    }
}